<template>
  <div class="all">
    <div ref="sidebar_container" class="sidebar_container">
      <el-collapse v-model="activeNames" style="height: 100%; overflow: scroll">
        <el-collapse-item
          title="模型列表"
          name="1"
          style="overflow: scroll; margin-bottom: -15px"
        >
          <ul>
            <li
              v-for="(item, index) in modellist"
              :key="index"
              class="toolItem"
              :ref="`modelItem${index}`"
            >
              {{ item.title }}
            </li>
          </ul>
        </el-collapse-item>
        <el-collapse-item title="数据列表" name="2" style="overflow: scroll">
          <ul>
            <li
              v-for="(item, index) in datalist"
              :key="item.title"
              class="toolItem"
              :ref="`dataItem${index}`"
            >
              {{ item.title }}
            </li>
          </ul>
        </el-collapse-item>
      </el-collapse>
    </div>
    <div class="graph_container">
      <div ref="graph" id="graphContainer"></div>
    </div>
  </div>
</template>

<script>
// import { mxGraph } from "mxgraph-js";
import { toolItems } from "./toolbarItems";
const closeIcon = require("@/assets/icon/delete.png");
// import * as R from "ramda";
import mx from "mxgraph";
import _ from "lodash";
import * as R from "ramda";
let graph = null;
const mxgraph = mx({});
const {
  mxStencilRegistry,
  mxStencil,
  mxEvent,
  mxGraph,
  mxEditor,
  mxUtils,
  mxRubberband,
  mxKeyHandler,
  mxGraphHandler,
  mxConstants,
  mxImage,
  mxCellState,
  mxConnectionHandler,
  mxCodec,
  mxRectangleShape,
  mxPoint,
  mxClipboard,
  mxCellTracker,
  mxUndoManager,
  mxClient,
  mxEdgeHandler,
  mxPerimeter,
  mxOutline,
  mxEventObject,
  mxGeometry,
  mxCell,
  mxShape,
  mxConstraintHandler,
  mxEllipse,
  // mxTriangle,
  mxFastOrganicLayout,
  mxHierarchicalLayout,
  mxCompactTreeLayout,
  mxMorphing,
  mxCircleLayout,
  mxSvgCanvas2D,
  mxImageExport,
  mxConnectionConstraint,
  mxPolyline,
  mxVertexHandler,
  mxRectangle,
  mxImageShape,
  ActiveXObject,
  mxEdgeStyle,
} = mxgraph;
export default {
  data() {
    return {
      graph: null,
      editor: null,
      palettes: {},
      graphXml: "",
      activeNames: ["1", "2", "3"],
      isNode: false,
      cellStyle: "",
      graphX: 100,
      graphY: 10,
      undoMng: "",
      textValue: "",
      uploadDataVisible: false,
      isOutputXml: false,
      edgeStyle: "orthogonalEdgeStyle",
      outline: "",
      idSeed: 0,
      normalIdSeed: 0,
      toolItems: toolItems,
      // configOrder: 0,
      jsonData: {
        cells: {
          nodes: [],
          groups: [],
        },
        edges: [],
      },
      showBackground: true,
      currentNormalType: {},
      normalTypePosition: {
        top: "0",
        left: "0",
      },
      modellist: [
        {
          title: "SWAT",
          input: [
            "Dem.tif",
            "Population.tif",
            "Landuse.tif",
            "Rain.xlsx",
            "Area.shp",
          ],
          output: [
            "HydroResponseUnitData.tif",
            "FullHruShape.tif",
            "SWATBasinShapeGen.tif",
            "SWATRUN.tif",
          ],
        },
        {
          title: "SWMM",
          input: [
            "dem.tif",
            "population.tif",
            "landuse.tif",
            "rain.xlsx",
            "area.shp",
          ],
          output: ["result.tif"],
        },
        {
          title: "SEIMS",
          input: [
            "dem.tif",
            "population.tif",
            "landuse.tif",
            "rain.xlsx",
            "area.shp",
          ],
          output: ["result.tif"],
        },
        {
          title: "FvCOM",
          input: [
            "dem.tif",
            "population.tif",
            "landuse.tif",
            "rain.xlsx",
            "area.shp",
          ],
          output: ["result.tif"],
        },
        {
          title: "MODFLOW",
          input: [
            "dem.tif",
            "population.tif",
            "landuse.tif",
            "rain.xlsx",
            "area.shp",
          ],
          output: ["result.tif"],
        },
        {
          title: "LISFLOOD-FP",
          input: [
            "dem.tif",
            "population.tif",
            "landuse.tif",
            "rain.xlsx",
            "area.shp",
          ],
          output: ["result.tif"],
        },
      ],
      datalist: [
        {
          title: "dem.tif",
        },
        {
          title: "population.tif",
        },
        {
          title: "landuse.tif",
        },
        {
          title: "rain.xlsx",
        },
        {
          title: "area.shp",
        },
        {
          title: "result.tif",
        },
      ],
    };
  },
  methods: {
    instancemodelCell(dropCell, toolItem, x, y, item) {
      const drop = !R.isNil(dropCell);
      // drop && this.$message.info(`${toolItem['title']}节点入${dropCell.title}`);
      const styleObj = toolItem["style"] || {};
      const style = Object.keys(styleObj)
        .map((key) => `${key}=${styleObj[key]}`)
        .join(";");
      const realX = drop ? x - dropCell.geometry.x : x;
      const realY = drop ? y - dropCell.geometry.y : y;
      const { height, width } = toolItem;
      const parent = drop ? dropCell : graph.getDefaultParent();
      const isHtml = Object.is("1", toolItem["style"]["html"]);
      const tmpIndex = Date.now();
      console.log(item);
      // const value = isHtml
      //   ? this.generatemodelUMLShapeHTML(item, tmpIndex)
      //   : null;
      graph.getModel().beginUpdate();
      try {
        const vertex = graph.insertVertex(
          parent,
          null,
          item.title,
          realX,
          realY,
          width,
          height,
          style
        );
        let curheight = 0;
        const vertex1 = graph.insertVertex(
          vertex,
          null,
          "<i>input</i>",
          0,
          curheight,
          width,
          14,
          "shape=text;fontSize=14;strokeColor=none;fillColor=none;align=left;verticalAlign=top;align=center;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];perimeter=fixedPerimeter;"
        );
        curheight += 40;
        const vertex2 = graph.insertVertex(
          vertex,
          null,
          "",
          0,
          curheight,
          width,
          12,
          "shape=line;strokeWidth=1;fillColor=none;strokeColor=#000;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];"
        );
        curheight += 12;

        for (let i = 0; i < item.input.length; i++) {
          graph.insertVertex(
            vertex,
            null,
            `-> ${item.input[i]}`,
            0,
            curheight,
            width,
            12,
            "shape=text;fontSize=12;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];"
          );
          curheight += 12;

          graph.insertVertex(
            vertex,
            null,
            "",
            0,
            curheight,
            width,
            12,
            "shape=line;strokeWidth=1;fillColor=none;strokeColor=#000;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];"
          );
          curheight += 12;
        }

        const vertex3 = graph.insertVertex(
          vertex,
          null,
          "<i>output</i>",
          0,
          curheight,
          width,
          14,
          "shape=text;fontSize=14;strokeColor=none;fillColor=none;align=left;verticalAlign=top;align=center;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];"
        );
        curheight += 14;

        const vertex4 = graph.insertVertex(
          vertex,
          null,
          "",
          0,
          curheight,
          width,
          12,
          "shape=line;strokeWidth=1;fillColor=none;strokeColor=#000;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];"
        );
        curheight += 12;

        for (let i = 0; i < item.output.length; i++) {
          graph.insertVertex(
            vertex,
            null,
            `-> ${item.output[i]}`,
            0,
            curheight,
            width,
            12,
            "shape=text;fontSize=12;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];"
          );
          curheight += 12;

          graph.insertVertex(
            vertex,
            null,
            "",
            0,
            curheight,
            width,
            12,
            "shape=line;strokeWidth=1;fillColor=none;strokeColor=#000;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];"
          );
          curheight += 12;
        }

        // graph.getModel().setOverlap(vertex1, false);
        // graph.getModel().setOverlap(vertex2, false);
        // vertex["title"] = toolItem["title"];
        // vertex["dropAble"] = toolItem["dropAble"];
        // vertex["id"] = toolItem["id"];
        // vertex["isGroup"] = toolItem["id"].includes("group") ? true : false;
        // 设置连接点
        // cell['constraints'] = toolItem['constraints']
        this.$nextTick(() => {
          const createdCallback = toolItem["created"];
          if (createdCallback instanceof Function) {
            createdCallback(graph, vertex, tmpIndex);
          }
        });
      } finally {
        graph.getModel().endUpdate();
      }
    },
    instancedataCell(dropCell, toolItem, x, y, item) {
      console.log(12313);
      const drop = !R.isNil(dropCell);
      // drop && this.$message.info(`${toolItem['title']}节点入${dropCell.title}`);
      const styleObj = toolItem["style"] || {};
      const style = Object.keys(styleObj)
        .map((key) => `${key}=${styleObj[key]}`)
        .join(";");
      const realX = drop ? x - dropCell.geometry.x : x;
      const realY = drop ? y - dropCell.geometry.y : y;
      const { height, width } = toolItem;
      const parent = drop ? dropCell : graph.getDefaultParent();
      const isHtml = Object.is("1", toolItem["style"]["html"]);
      const tmpIndex = Date.now();
      console.log(item);
      const value = isHtml
        ? this.generatedataUMLShapeHTML(item, tmpIndex)
        : null;
      graph.getModel().beginUpdate();
      try {
        const vertex = graph.insertVertex(
          parent,
          null,
          value,
          realX,
          realY,
          width,
          height,
          style
        );
        // vertex["title"] = toolItem["title"];
        // vertex["dropAble"] = toolItem["dropAble"];
        // vertex["id"] = toolItem["id"];
        // vertex["isGroup"] = toolItem["id"].includes("group") ? true : false;
        // 设置连接点
        // cell['constraints'] = toolItem['constraints']
        this.$nextTick(() => {
          const createdCallback = toolItem["created"];
          if (createdCallback instanceof Function) {
            createdCallback(graph, vertex, tmpIndex);
          }
        });
      } finally {
        graph.getModel().endUpdate();
      }
    },
    generateUMLShapeHTML(toolItem, index) {
      const htmlStr = `
        <p style="margin: 4px 0 0 0;text-align: center">
            <i>&lt;&lt;Interface&gt;&gt;</i>
            <br/>
            <b>Interface</b>
        </p>
        <hr/>
        <p style="margin: 0; margin-left: 4px">
            + field1: Type
            <br/>
            + field2: Type
        </p>
        <hr/>
        <p style="margin: 0; margin-left: 4px">
            + method1(Type): Type
            <br/>
            + method2(Type, Type): Type
        </p>`.replace(/[\r\n]/g, "");

      return `
        <div style="margin: 0; padding: 0; height: 100%; display: flex; flex-direction: column; align-items: center;">
            <div style="font-size: 1.2rem; color: cornsilk; font-weight: 600;"></div>
            <div class="uml-shape">${htmlStr}</div>
            <div class="attributes">Attributes: </div>
            <img style="height: 20px; width: 20px; cursor: pointer;" class="deleteIcon_${index}" src="${closeIcon}" alt=""/>
        </div>
    `.replace(/[\r\n]/g, "");
    },
    generatedataUMLShapeHTML(item, index) {
      let htmlStr = `
        <p style="margin: 4px 0 0 0;text-align: center">
            <span style="font-size: 15px; font-weight: 600;">${item.title}</span>
            <br/>
        </p>`.replace(/[\r\n]/g, "");
      return htmlStr;
    },
    generatemodelUMLShapeHTML(item, index) {
      let inputStr = `<p style="margin: 0; margin-left: 4px;text-align: left;font-size:12px;text-align: center"><i>INPUT</i><br/><hr style="border: none;border-top: 2px solid #1F1F1F;" />`;
      item.input.forEach((input) => {
        inputStr += `<div style="margin-left: 15px;text-align: left;margin-top: 8px;font-size:15px;border:";>-><i>${input}</i></div><hr style="border: none;border-top: 2px solid #1F1F1F;" />`;
      });
      inputStr += `</p>`;
      inputStr = inputStr.replace(/[\r\n]/g, "");
      let outputStr = `<p style="margin: 0; margin-left: 4px;text-align: left;font-size:12px;text-align: center"><i>OUTPUT</i><br/><hr style="border: none;border-top: 2px solid #1F1F1F;" />`;
      item.output.forEach((output) => {
        outputStr += `<div style="margin-left: 15px;text-align: left;margin-top: 8px;font-size:15px";><-<i>${output}</i></div><hr style="border: none;border-top: 2px solid #1F1F1F;" />`;
      });
      outputStr += `</p>`;
      outputStr = outputStr.replace(/[\r\n]/g, "");
      let htmlStr = `<div">
        <p style="text-align: center;margin-top:0;margin-bottom: 5px;border: 1px solid #CD8C88;background-color: #F9CFCD;border-top-left-radius: 4px;border-top-right-radius: 4px">
            <span style="font-size: 25px;font-weight: 600">${item.title}</span>
        </p>
        ${inputStr}
        ${outputStr}</div>`.replace(/[\r\n]/g, "");
      return htmlStr;
    },
    createGraph() {
      // 创建graph
      // 方式一：直接构建graph实例
      // graph = new mxGraph(this.$refs.container)
      // this.editor = new mxEditor();
      // graph = this.editor.graph;
      // this.editor.setGraphContainer(this.$refs.graph);
      graph = new mxGraph(this.$refs.graph);
      // 配置默认全局样式
      this.configureStylesheet(graph);
      // 去锯齿效果
      mxRectangleShape.prototype.crisp = true;
      // 定义全局变量，如。用于触发建立新的连接的活动区域的最小尺寸（以像素为单位），该部分（100％）的小区区域被用于触发新的连接，以及一些窗口和“下拉菜菜单选择
      mxConstants.MIN_HOTSPOT_SIZE = 16;
      mxConstants.DEFAULT_HOTSPOT = 1;

      //cell创建支持传入html
      graph.setHtmlLabels(true);
      graph.setDropEnabled(true);
      graph.setSplitEnabled(false);
      graph.setConnectable(true);
      // 有效的拖放操作，则返回true
      graph.isValidDropTarget = (target, cells, evt) => {
        if (graph.isSplitEnabled() && graph.isSplitTarget(target, cells, evt)) {
          console.log("拖放");
          return true;
        }
      };

      // 禁用分组的收缩功能 方法1:
      // graph.isCellFoldable = (cell) => {
      //   return false
      // }
      // 禁用分组的收缩功能 方法2:
      graph.foldingEnabled = false;
      // 组内的子元素是否随父元素变化而变化
      graph.recursiveResize = true;

      // 设置连线时的预览路径及样式
      graph.connectionHandler.createEdgeState = () => {
        // 设置预览的连接线,第三个参数为连接成功后连接线上的label
        var edge = graph.createEdge(null, null, null, null, null);
        // edge.style += `;edgeStyle=orthogonalEdgeStyle `
        return new mxCellState(graph.view, edge, graph.getCellStyle(edge));
      };

      // 是否开启旋转
      mxVertexHandler.prototype.livePreview = true;
      mxVertexHandler.prototype.rotationEnabled = true;
      // 设置旋转按钮
      mxVertexHandler.prototype.createSizerShape = function (
        bounds,
        index,
        fillColor
      ) {
        if (this.handleImage != null) {
          bounds = new mxRectangle(
            bounds.x,
            bounds.y,
            this.handleImage.width,
            this.handleImage.height
          );
          let shape = new mxImageShape(bounds, this.handleImage.src);
          // Allows HTML rendering of the images
          shape.preserveImageAspect = true;
          return shape;
        } else if (index == mxEvent.ROTATION_HANDLE) {
          // return new mxDoubleEllipse(bounds, fillColor || mxConstants.HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR);
          // 设置旋转图标
          bounds = new mxRectangle(bounds.x, bounds.y, 15, 15);
          let rotationShape = new mxImageShape(bounds, "icon/rotate.svg");
          rotationShape.preserveImageAspect = true;
          return rotationShape;
        } else {
          return new mxRectangleShape(
            bounds,
            fillColor || mxConstants.HANDLE_FILLCOLOR,
            mxConstants.HANDLE_STROKECOLOR
          );
        }
      };
      // 设置旋转角度（解决默认旋转180度的bug）
      mxVertexHandler.prototype.getRotationHandlePosition = function () {
        let padding = this.getHandlePadding();
        return new mxPoint(
          this.bounds.x +
            this.bounds.width -
            this.rotationHandleVSpacing +
            padding.x / 2,
          this.bounds.y + this.rotationHandleVSpacing - padding.y / 2
        );
      };

      // 设置默认组
      // groupBorderSize 设置图形和它的子元素的边距
      //   let group = new mxCell(
      //     "Group",
      //     new mxGeometry(),
      //     "group;fontColor=white;"
      //   );
      //   group.setVertex(true);
      //   // 设置组可连接
      //   group.setConnectable(true);
      // group.setCellsResizable(false);
      //   this.editor.defaultGroup = group;
      //   this.editor.groupBorderSize = 80;

      // 是否根元素
      graph.isValidRoot = function (cell) {
        return this.isValidDropTarget(cell);
      };
      graph.setConnectable(true);

      // // 是否可以被选中
      // graph.isCellSelectable = function (cell) {
      //   return !this.isCellLocked(cell);
      // };

      // 返回元素
      graph.getLabel = function (cell) {
        var tmp = mxGraph.prototype.getLabel.apply(this, arguments); // "supercall"
        if (this.isCellLocked(cell)) {
          // 如元素被锁定 返回空标签
          return "";
        } else if (this.isCellCollapsed(cell)) {
          var index = tmp.indexOf("</h1>");
          if (index > 0) {
            tmp = tmp.substring(0, index + 5);
          }
        }
        return tmp;
      };

      // 目标是否有效
      graph.isValidDropTarget = function (cell) {
        // console.log(cell, cells, evt);
        return this.isSwimlane(cell);
      };

      // 是否根元素
      graph.isValidRoot = function (cell) {
        return this.isValidDropTarget(cell);
      };

      // 是否可以被选中
      graph.isCellSelectable(true);

      // 允许重复连接
      graph.setMultigraph(true);
      // 禁止连接线晃动(即连线两端必须在节点上)
      // graph.setAllowDanglingEdges(false);
      // 允许连线的目标和源是同一元素
      graph.setAllowLoops(false);
      //边被拖动时始终保持连接
      // graph.setDisconnectOnMove(false);
      // 选择基本元素开启
      graph.setEnabled(true);
      // 动态改变样式
      graph.getView().updateStyle = true;
      // 鼠标框选
      this.rubberBand = new mxRubberband(graph);
      graph.setResizeContainer(true);

      // 开启画布平滑移动
      graph.setPanning(true);
      //   graph.setPanning = true;
      // 开启提示
      graph.setTooltips(false);
      // 允许连线
      graph.setConnectable(true);
      //移动元素的步长
      graph.gridSize = 3;
      graph.setBorder(160);

      // 开启方块上的文字编辑功能
      graph.setCellsEditable(true);
      // 禁止双击修改内容(弃用)
      // graph.dblClick = (evt, cell) => {
      //   var model = graph.getModel();
      //   if (model.isVertex(cell)) {
      //     return false;
      //   }
      // };
      // Disables synchronous loading of resources
      // 可用于禁用HTML的泳道标签，避免冲突(返回false即可)
      // 判断是否为泳道标签
      // graph.isHtmlLabel = function (cell) {
      //   return this.isSwimlane(cell);
      // };
      //准备撤销还原功能
      // 构造具有给定历史记录大小的新撤消管理器。默认100步
      this.undoMng = new mxUndoManager();
      let listener = (sender, evt) => {
        this.undoMng.undoableEditHappened(evt.getProperty("edit"));
      };
      graph.getModel().addListener(mxEvent.UNDO, listener);
      graph.getView().addListener(mxEvent.UNDO, listener);
      // 创建缩略图
      //   this.outline = new mxOutline(
      //     graph,
      //     this.$refs.styleSelect.$refs.showMap
      //   );
      if (graph == null || graph == undefined) {
        return;
      }
      // 从value中获取显示的内容(如果节点的value为空则显示节点的title)
      graph.convertValueToString = (cell) => {
        return cell["value"] ? cell["value"] : cell["title"];
      };
    },
    configureStylesheet(graph) {
      // console.log(123123123123);
      // 设置节点的文字可被移动
      graph.vertexLabelsMovable = false;
      // 设置鼠标悬浮至节点或者连线时高亮显示的颜色
      new mxCellTracker(graph, "#409eff");
      var style = new Object();
      style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_LABEL;
      style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter;
      style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_MIDDLE;
      style[mxConstants.STYLE_ALIGN] = mxConstants.ALIGN_CENTER;
      style[mxConstants.STYLE_IMAGE_ALIGN] = mxConstants.ALIGN_CENTER;
      style[mxConstants.STYLE_IMAGE_VERTICAL_ALIGN] = mxConstants.ALIGN_CENTER;
      // style[mxConstants.STYLE_SPACING_TOP] = 6;
      style[mxConstants.STYLE_SPACING_LEFT] = 5;
      // style[mxConstants.STYLE_GRADIENTCOLOR] = 'skyblue'; // 渐变颜色
      style[mxConstants.STYLE_STROKECOLOR] = "#5d65df"; // 线条颜色
      style[mxConstants.STYLE_FILLCOLOR] = "#FFFFFF";
      style[mxConstants.STYLE_FONTCOLOR] = "#1d258f"; // 字体颜色
      style[mxConstants.STYLE_FONTFAMILY] = "Verdana"; // 字体风格
      style[mxConstants.STYLE_FONTSIZE] = "12"; // 字体大小
      style[mxConstants.STYLE_FONTSTYLE] = "0"; // 斜体字
      style[mxConstants.WORD_WRAP] = "normal"; // 文字换行    word-break: break-all;
      style[mxConstants["word-break"]] = "break-all"; // 文字换行
      style[mxConstants.STYLE_WHITE_SPACE] = "wrap"; // 文字换行
      style[mxConstants.STYLE_ROUNDED] = false; // 圆角
      style[mxConstants.STYLE_IMAGE_WIDTH] = "28"; // 图片宽度
      style[mxConstants.STYLE_IMAGE_HEIGHT] = "28"; // 图片高度
      style[mxConstants.STYLE_OPACITY] = "100"; // 节点透明度(不包含字体)
      graph.getStylesheet().putDefaultVertexStyle(style);

      style = new Object();
      style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_SWIMLANE;
      style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter;
      style[mxConstants.STYLE_ALIGN] = mxConstants.ALIGN_CENTER;
      style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_TOP;
      style[mxConstants.STYLE_FILLCOLOR] = "#409eff";
      // style[mxConstants.STYLE_GRADIENTCOLOR] = '#409eff';
      style[mxConstants.STYLE_STROKECOLOR] = "#409eff";
      style[mxConstants.STYLE_FONTCOLOR] = "#000000";
      style[mxConstants.STYLE_ROUNDED] = false;
      style[mxConstants.STYLE_OPACITY] = "80";
      style[mxConstants.STYLE_STARTSIZE] = "30";
      style[mxConstants.STYLE_FONTSIZE] = "16";
      style[mxConstants.STYLE_FONTSTYLE] = 1;
      graph.getStylesheet().putCellStyle("group", style);

      style = new Object();
      style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_IMAGE;
      style[mxConstants.STYLE_FONTCOLOR] = "#774400";
      style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter;
      style[mxConstants.STYLE_PERIMETER_SPACING] = "6";
      style[mxConstants.STYLE_ALIGN] = mxConstants.ALIGN_LEFT;
      style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_MIDDLE;
      style[mxConstants.STYLE_FONTSIZE] = "10";
      style[mxConstants.STYLE_FONTSTYLE] = 2;
      style[mxConstants.STYLE_IMAGE_WIDTH] = "16";
      style[mxConstants.STYLE_IMAGE_HEIGHT] = "16";
      style[mxConstants.STYLE_BACKGROUNDCOLOR] = "transparent";
      graph.getStylesheet().putCellStyle("port", style);

      style = graph.getStylesheet().getDefaultEdgeStyle();
      style[mxConstants.STYLE_LABEL_BACKGROUNDCOLOR] = "#FFFFFF";
      style[mxConstants.STYLE_STROKEWIDTH] = "2";
      style[mxConstants.STYLE_ROUNDED] = true;
      // 获取全局Edge、label样式
      var edgeStyle = graph.getStylesheet().getDefaultEdgeStyle();
      let labelStyle = graph.getStylesheet().getDefaultVertexStyle();
      // labelStyle[mxConstants.STYLE_WHITE_SPACE] = 'wrap'; //自动换行
      console.log(labelStyle, "labelStyle");
      console.log(edgeStyle, "edgeStyle");
      // 设置连线风格(设置为正交折线)
      edgeStyle["edgeStyle"] = "orthogonalEdgeStyle";
      edgeStyle[mxConstants.STYLE_EDGE] = mxEdgeStyle.OrthConnector;
      edgeStyle[mxConstants.STYLE_STROKECOLOR] = "#000000";
      // 设置边缘样式为直角（非圆角）
      edgeStyle[mxConstants.STYLE_ROUNDED] = false;

      // 选中 cell/edge 后的伸缩大小的点/拖动连线位置的点的颜色
      // style[mxConstants.STYLE_WHITE_SPACE] = 'wrap'

      mxConstants.HANDLE_FILLCOLOR = "#409eff";
      mxConstants.HANDLE_STROKECOLOR = "transparent";
      mxConstants.STYLE_ANCHOR_POINT_DIRECTION = "anchorPointDirection";
      mxConstants.STYLE_STYLE_ROTATION = "rotation";
      // 是否缩放网格
      mxGraphHandler.prototype.scaleGrid = true;
      mxGraph.prototype.pageBreakDashed = false;

      // 指定是否应使用其他单元格对齐当前所选内容的右侧，中间或左侧。默认为false。
      mxGraphHandler.prototype.guidesEnabled = true;
      mxGraphHandler.prototype.htmlPreview = false;
      mxGraphHandler.prototype.allowLivePreview = true;
      // 指定预览形状的颜色。默认为黑色。
      mxGraphHandler.prototype.previewColor = "red";
      // 应该使用实时预览的最大单元数。默认值为0，表示没有实时预览。
      mxGraphHandler.prototype.maxLivePreview = 100;

      // Alt 按下禁用导航线
      mxGraphHandler.prototype.useGuidesForEvent = function (me) {
        return !mxEvent.isAltDown(me.getEvent());
      };
      // 导航线颜色
      mxConstants.GUIDE_COLOR = "#1a73e8";
      // 导航线宽度
      mxConstants.GUIDE_STROKEWIDTH = 2;
      // 导航线自动连接到目标
      mxEdgeHandler.prototype.snapToTerminals = true;
      // 选中线条时的虚线颜色
      mxConstants.EDGE_SELECTION_COLOR = "#99ccff";
      // mxConstants.DEFAULT_INVALID_COLOR = 'yellow';
      // mxConstants.INVALID_CONNECT_TARGET_COLOR = 'yellow';
      // 连线(未满足连线要求)时预览的颜色
      mxConstants.INVALID_COLOR = "#99ccff";
      // 连线(满足连线要求)时预览的颜色
      mxConstants.VALID_COLOR = "blue";
      // mxConstants.GUIDE_COLOR = 'yellow';
      // mxConstants.LOCKED_HANDLE_FILLCOLOR = '#24bcab';
      // 选中节点时选中框的颜色
      mxConstants.VERTEX_SELECTION_COLOR = "#99ccff";

      //折叠-/展开+图标大小
      // mxGraph.prototype.collapsedImage = new mxImage('images/collapsed.gif', 15, 15);
      // mxGraph.prototype.expandedImage = new mxImage('images/expanded.gif', 15, 15);

      // 配置节点中心的连接图标(注釋掉即可指定錨點連接到另一個節點的錨點上)
      //   mxConnectionHandler.prototype.connectImage = new mxImage(
      //     "./icon/connectionpoint.png",
      //     14,
      //     14
      //   );
      // 显示中心端口图标
      //   graph.connectionHandler.targetConnectImage = false;
      // 是否开启浮动自动连接
      graph.connectionHandler.isConnectableCell = function () {
        return true;
      };

      console.log(graph.connectionHandler);

      const pointImg = require("@/assets/point.gif");

      graph.connectionHandler.constraintHandler.pointImage = new mxImage(
        pointImg,
        10,
        10
      );
      // 设定锚点的位置、可编辑状态和图标
      //   mxConstraintHandler.prototype.pointImage = new mxImage(
      //     "icon/dot.svg",
      //     10,
      //     10
      //   );
      // 设置锚点上的高亮颜色
      mxConstraintHandler.prototype.createHighlightShape = function () {
        return new mxEllipse(null, "#409eff99", "#409eff99", 15);
      };

      mxShape.prototype.constraints = [
        new mxConnectionConstraint(new mxPoint(0, 0.5), true),
        new mxConnectionConstraint(new mxPoint(0.5, 0), true),
        new mxConnectionConstraint(new mxPoint(0.5, 1), true),
        new mxConnectionConstraint(new mxPoint(1, 0.5), true),
        // new mxConnectionConstraint(new mxPoint(0, 0.25), true),
        // new mxConnectionConstraint(new mxPoint(0, 0.5), true),
        // new mxConnectionConstraint(new mxPoint(0, 0.75), true),
        // new mxConnectionConstraint(new mxPoint(1, 0), true),
        // new mxConnectionConstraint(new mxPoint(1, 0.25), true),
        // new mxConnectionConstraint(new mxPoint(1, 0.5), true),
        // new mxConnectionConstraint(new mxPoint(1, 0.75), true),
        // new mxConnectionConstraint(new mxPoint(0, 1), true),
        // new mxConnectionConstraint(new mxPoint(0.25, 1), true),
        // new mxConnectionConstraint(new mxPoint(0.5, 1), true),
        // new mxConnectionConstraint(new mxPoint(0.75, 1), true),
        // new mxConnectionConstraint(new mxPoint(1, 1), true),
      ];
      mxPolyline.prototype.constraints = null;
    },
    configKeyEvent() {
      // 启动盘事件键
      this.keyHandler = new mxKeyHandler(graph);
      // 键盘按下删除键绑定事件
      this.keyHandler.bindKey(46, () => {
        this.deleteNode();
      });
      this.keyHandler.bindControlKey(65, () => {
        graph.selectAll();
      });
      this.keyHandler.bindControlKey(67, () => {
        this.copy();
      });
      this.keyHandler.bindControlKey(88, () => {
        this.cut();
      });
      this.keyHandler.bindControlKey(86, () => {
        this.paste();
      });
      this.keyHandler.bindControlKey(89, () => {
        this.goForward();
      });
      this.keyHandler.bindControlKey(90, () => {
        this.goBack();
      });
    },
    makemodelDraggable() {
      const modelItems = [];
      for (let i = 0; i < this.modellist.length; i++) {
        modelItems.push(this.$refs["modelItem" + i]);
      }
      if (!(modelItems instanceof Array)) {
        console.log(123);
        return;
      }
      modelItems.forEach((item, index) => {
        const toolItem = this.toolItems[2];
        const { height, width } = toolItem;
        const modelitem = this.modellist[index];
        // drop的处理函数
        const dropHandler = (graph, evt, cell, x, y) => {
          this.instancemodelCell(cell, toolItem, x, y, modelitem);
        };
        // 创建拖拽时的预览图形
        const createDragPreview = () => {
          const elt = document.createElement("div");

          elt.style.border = "2px dotted black";
          elt.style.width = `${width}px`;
          elt.style.height = `${height}px`;
          return elt;
        };
        // 获取拖放的对象
        const getDropTarget = (graph, x, y) => {
          const cell = graph.getCellAt(x, y);

          return R.propOr(null, "dropAble", cell) ? cell : null;
        };

        mxUtils.makeDraggable(
          item,
          graph,
          dropHandler,
          createDragPreview(index),
          0,
          0,
          false,
          true,
          true,
          getDropTarget
        );
      });
    },

    makedataDraggable() {
      const dataItems = [];
      for (let i = 0; i < this.datalist.length; i++) {
        dataItems.push(this.$refs["dataItem" + i]);
      }
      console.log(this.$refs);
      if (!(dataItems instanceof Array)) {
        console.log(123);
        return;
      }
      console.log(dataItems);
      dataItems.forEach((item, index) => {
        const toolItem = this.toolItems[0];
        const dataitem = this.datalist[index];
        const { height, width } = toolItem;
        // drop的处理函数
        const dropHandler = (graph, evt, cell, x, y) => {
          this.instancedataCell(cell, toolItem, x, y, dataitem);
        };
        // 创建拖拽时的预览图形
        const createDragPreview = () => {
          const elt = document.createElement("div");

          elt.style.border = "2px dotted black";
          elt.style.width = `${width}px`;
          elt.style.height = `${height}px`;
          return elt;
        };
        // 获取拖放的对象
        const getDropTarget = (graph, x, y) => {
          const cell = graph.getCellAt(x, y);

          return R.propOr(null, "dropAble", cell) ? cell : null;
        };

        mxUtils.makeDraggable(
          item,
          graph,
          dropHandler,
          createDragPreview(index),
          0,
          0,
          false,
          true,
          true,
          getDropTarget
        );
      });
    },
    eventCenter() {
      // 给graph添加事件
      // 监听自定义事件
      graph.addListener(mxEvent.NORMAL_TYPE_CLICKED, (sender, evt) => {
        console.log(evt);
        let cell = evt.properties.cell.state.cell;
        this.currentNormalType = cell;
      });
      // graph.addListener(mxEvent.VERTEX_START_MOVE, (sender, evt) => {
      //   console.log('VERTEX_START_MOVE', sender, evt);
      // });
      // 画布平移事件
      graph.addListener(mxEvent.PAN, (sender, evt) => {
        console.log("画布平移了", sender, evt);
      });
      // 新增节点事件
      graph.addListener(mxEvent.ADD_CELLS, (sender, evt) => {
        this.$nextTick(() => {
          let addCell = evt.properties.cells[0];
          if (addCell.vertex) {
            // 判断是否为组节点
            if (addCell.isGroup) {
              this.$message.info("添加了一个组");
              let groupObj = _.pick(addCell, [
                "id",
                "title",
                "parent",
                "geometry",
              ]);
              this.jsonData["cells"]["groups"].push(groupObj);
            } else {
              let nodeObj = _.pick(addCell, [
                "id",
                "title",
                "parent",
                "geometry",
              ]);
              this.jsonData["cells"]["nodes"].push(nodeObj);
              this.$message.info("添加了一个节点");
            }
            //  向jsonData中更新数据
          } else if (addCell.edge) {
            console.log(addCell);
            let lineObj = _.pick(addCell, [
              "id",
              "edge",
              "source",
              "parent",
              "geometry",
              "value",
            ]);
            this.jsonData["edges"].push(lineObj);
            this.$message.info("添加了一条线");
          }
        });
      });

      //拖动节点的事件
      graph.addListener(mxEvent.CELLS_MOVED, (sender, evt) => {
        // console.log(graph, 'graph');
        let cellsName = [];
        this.$nextTick(() => {
          evt.properties.cells.forEach((item) => {
            item.parent.id.includes("group") && cellsName.push(item.title);
          });
          evt.properties.cells[0].parent.id !== "1" &&
            this.$message.info(
              `${[...cellsName]}节点进入${evt.properties.cells[0].parent.title}`
            );
        });
      });
      // 删除节点触发事件
      graph.addListener(mxEvent.CELLS_REMOVED, (sender, evt) => {
        this.$nextTick(() => {
          let removeCells = evt.properties.cells;
          console.log(removeCells, "removeCells");
          removeCells.forEach((item) => {
            // 拿每一个cellId在jsonData中进行遍历,并进行移除
            if (item.vertex) {
              // 判断是否为组节点
              if (item.isGroup) {
                this.$message.info(`移除了${item.id}组`);
                this.jsonData["cells"]["groups"].splice(
                  this.jsonData["cells"]["groups"].findIndex((jsonItem) => {
                    return jsonItem.id === item.id;
                  }),
                  1
                );
              } else {
                this.$message.info(`移除${item.id}节点`);
                this.jsonData["cells"]["nodes"].splice(
                  this.jsonData["cells"]["nodes"].findIndex((jsonItem) => {
                    return jsonItem.id === item.id;
                  }),
                  1
                );
              }
            } else if (item.edge) {
              this.$message.info("移除了线");
              this.jsonData["edges"].splice(
                this.jsonData["edges"].findIndex((jsonItem) => {
                  return jsonItem.id === item.id;
                }),
                1
              );
            }
            // this.jsonData.forEach(item)
          });
        });
      });
    },
    configMouseEvent() {
      graph.addMouseListener({
        currentState: null,
        previousStyle: null,

        mouseDown: (sender, evt) => {
          if (!evt.state) {
            console.log("点击了画布");
            return;
          } else if (evt.state.cell.edge) {
            console.log("点击了连线");
            return;
          }

          const cell = evt.state.cell;
          let clickNormalType = false;
          if (cell.style !== undefined) {
            clickNormalType = cell.style.includes("normalType");
          }
          if (clickNormalType) {
            // 使用 mxGraph 事件中心，注册自定义事件
            graph.fireEvent(
              new mxEventObject(mxEvent.NORMAL_TYPE_CLICKED, "cell", evt)
            );
          } else {
            return;
          }
        },

        mouseMove: (sender, me) => {
          // console.log('mouseMove')
          this.graphX = Math.ceil(me.graphX);
          this.graphY = Math.ceil(me.graphY);
        },

        mouseUp: (sender, evt) => {
          console.log("mouseUp");
          if (evt.sourceState === undefined) {
            return false;
          } else {
            var cell = evt.sourceState.cell;
            // console.log(cell);
            //  var cell = graph.getSelectionCell();
            if (cell) {
              this.textValue = cell["value"] ? cell["value"] : cell["title"];
              console.log(this.textValue, "cellValue");
              console.log("cellValuie", cell);
              cell.vertex ? (this.isNode = true) : (this.isNode = false);
              var getcellStyle = cell.getStyle() ? cell.getStyle() : null;
              if (!this.isNode) {
                // 点击的不是节点
                getcellStyle
                  ? (this.edgeStyle = getcellStyle)
                  : "orthogonalEdgeStyle";
              } else {
                // 点击的是节点
                // console.log('getcellStyle', getcellStyle);
                if (getcellStyle) {
                  var arr = getcellStyle.split(";");
                  // arr.pop()
                  var styleObject = {};
                  arr.forEach((item) => {
                    styleObject[item.split("=")[0]] = item.split("=")[1];
                  });
                  this.cellStyle = styleObject;
                }
              }
            } else {
              this.$message.error("请选择节点或者连线");
            }
          }
        },
      });
    },
    //配置右键菜单栏
    configMenu() {
      // 禁用浏览器默认的右键菜单栏
      mxEvent.disableContextMenu(this.$refs.graph);
      graph.popupMenuHandler.factoryMethod = (menu) => {
        menu.addItem("输出所有节点", null, () => {
          Object.values(graph.getModel().cells).forEach((cell) => {
            console.log(cell);
          });
        });

        menu.addSeparator();
        menu.addItem("复制", null, () => {
          this.copy();
        });

        menu.addItem("粘贴", null, () => {
          this.paste();
        });

        menu.addSeparator();
        menu.addItem("组合", null, () => {
          this.enGroup();
        });

        menu.addItem("分解", null, () => {
          graph.ungroupCells(graph.getSelectionCells());
        });
        menu.addSeparator();
        menu.addItem("流动的线(测试)", null, () => {
          let cell = graph.getSelectionCell();
          if (cell && cell.edge) {
            let state = graph.view.getState(cell);
            state.shape.node
              .getElementsByTagName("path")[0]
              .removeAttribute("visibility");
            state.shape.node
              .getElementsByTagName("path")[0]
              .setAttribute("stroke-width", "6");
            state.shape.node
              .getElementsByTagName("path")[0]
              .setAttribute("stroke", "lightGray");
            state.shape.node
              .getElementsByTagName("path")[1]
              .setAttribute("class", "flow");
          } else {
            this.$message.error("请选择连线");
          }
        });
        menu.addSeparator();
        // menu.addItem('配置完成', null, () => {
        //   let cell = graph.getSelectionCell().children[0];
        //   let cellArrayStyle = cell.getStyle().split(';');
        //   cellArrayStyle.shift();
        //   let cellStyle = {};
        //   cellArrayStyle.forEach(item => {
        //     cellStyle[item.split('=')[0]] = item.split('=')[1];
        //   });
        //   let cellImage = cellStyle['image'].replace('unselect', 'selected');
        //   graph.setCellStyles(mxConstants.STYLE_IMAGE, cellImage, [cell]);
        //   graph.refresh(cell);
        // });
        menu.addItem("全选", null, () => {
          graph.selectAll();
        });
        menu.addItem("选中所有节点", null, () => {
          graph.selectCells(true, false);
        });
        menu.addItem("选中所有连线", null, () => {
          graph.selectCells(false, true);
        });
        // 分割符号
        menu.addSeparator();
        menu.addItem("修改样式", null, () => {
          var cell = graph.getSelectionCell();
          if (cell) {
            cell.vertex ? (this.isNode = true) : (this.isNode = false);
            var getcellStyle = cell.getStyle() ? cell.getStyle() : "";
            if (getcellStyle) {
              var arr = getcellStyle.split(";");
              //弹出最后一个空样式
              // arr.pop()
              var styleObject = {};
              arr.forEach((item) => {
                styleObject[item.split("=")[0]] = item.split("=")[1];
              });
              this.cellStyle = styleObject;
              // if (this.isNode) {

              // }
            }
          } else {
            this.$message.error("请选择节点或者连线");
          }
        });
      };
    },
    deleteNode() {
      // console.log(12312313123123123);
      var cells = graph.getSelectionCells();
      graph.removeCells([...cells]);
    },

    // 修改连线样式
    edgeChange(value) {
      try {
        var cell = graph.getSelectionCells();
        graph.setCellStyles("edgeStyle", value, [...cell]);
        let style = cell[0].style;
        let valStr = cell[0].value;
        graph.removeCells(cell);
        let parent = graph.getDefaultParent();
        let v1 = "";
        let v2 = "";
        // 获取ID单元
        parent["children"].forEach((item) => {
          item["id"] === cell[0].source.id ? (v1 = item) : false;
          item["id"] === cell[0].target.id ? (v2 = item) : false;
        });
        graph.getModel().beginUpdate();
        graph.insertEdge(parent, null, valStr, v1, v2, style);
        graph.getModel().endUpdate();
        this.$message.success("切换连线样式成功");
      } catch (error) {
        this.$message.error("切换连线样式失败");
        console.log(error);
      }
    },

    // 修改节点文本内容

    //复制
    copy() {
      let selectionCells = graph.getSelectionCells();
      mxClipboard.copy(graph, selectionCells);
    },
    //粘贴
    paste() {
      mxClipboard.paste(graph);
    },
    //剪切
    cut() {
      var cells = [];
      cells = graph.getSelectionCells();
      mxClipboard.cut(graph, cells);
    },
    // 前进
    goForward() {
      this.undoMng.redo();
    },

    // 撤退
    goBack() {
      this.undoMng.undo();
    },

    // 放大
    zoomIn() {
      graph.zoomIn();
    },

    // 缩小
    zoomOut() {
      graph.zoomOut();
    },

    // 等比例缩放
    autoSize() {
      // 方法一
      // this.editor.execute('actualSize');
      //方法二：
      graph.zoomActual();
      graph.fit(); //自适应
      graph.center(); //将画布放到容器中间
    },
    handleScroll(e) {
      if (e.wheelDelta === 120) {
        graph.zoomIn();
      } else {
        graph.zoomOut();
      }
    },
    insert() {
      var parent = graph.getDefaultParent();
      var model = graph.getModel();
      // 创建MXGraph实例
      // var graph = new mxGraph(container);

      // 获取默认父级元素，即图形的根元素
      // var parent = graph.getDefaultParent();

      // 添加一个Swimlane

      // Reusable cells
      model.beginUpdate();
      try {
        var field = new mxCell(
          "+ field: type",
          new mxGeometry(0, 0, 100, 26),
          "shape=text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;"
        );
        field.vertex = true;

        var divider = new mxCell(
          "",
          new mxGeometry(0, 0, 40, 8),
          "shape=line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;"
        );
        divider.vertex = true;
        var cell = new mxCell(
          "Classname",
          new mxGeometry(0, 0, 160, 90),
          "shape=swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;"
        );
        cell.vertex = true;
        // cell.insert(sb.cloneCell(field, '+ method(type): type'));
        graph.insertVertex(parent, null, cell, 0, 0, 200, 100);
        // cell.insert(field);
        // cell.insert(divider);
      } finally {
        model.endUpdate();
      }
      console.log(graph);
      // 获取Swimlane的位置和大小
    },
  },
  mounted() {
    // 检测浏览器兼容性
    if (!mxClient.isBrowserSupported()) {
      this.$message.error(
        "当前浏览器不支持拓扑图功能，请更换浏览器访问，建议使用Chrome浏览器访问!"
      );
    } else {
      // Overridden to define per-shape connection points
      mxGraph.prototype.getAllConnectionConstraints = function (terminal) {
        if (terminal != null && terminal.shape != null) {
          if (terminal.shape.stencil != null) {
            if (terminal.shape.stencil.constraints != null) {
              return terminal.shape.stencil.constraints;
            }
          } else if (terminal.shape.constraints != null) {
            return terminal.shape.constraints;
          }
        }
        return null;
      };
      this.createGraph();
      this.configMouseEvent();
      this.makemodelDraggable();
      this.makedataDraggable();
      this.configMenu();
      // mxEvent.disableContextMenu(this.$refs.container);
      this.configKeyEvent();
      // this.insert();
      // this.$nextTick(() => {
      //   this.insert();
      // });
      //   });
    }
    document
      .getElementById("graphContainer")
      .addEventListener("mousewheel", this.handleScroll, true); // 监听（绑定）滚轮滚动事件
  },
  unmounted() {
    document
      .getElementById("graphContainer")
      .removeEventListener("mousewheel", this.handleScroll); //  离开页面清除（移除）滚轮滚动事件
    graph.destroy();
  },
};
</script>

<style lang="less" scoped>
.all {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: row;
  overflow: hidden;
  .sidebar_container {
    width: 190px;
    height: 100%;
    padding-top: 15px;
    padding-left: 15px;
    padding-right: 5px;
    // background-color: aqua;
    ul {
      padding-left: 2px;
    }
    .toolItem {
      width: 95%;
      display: flex;
      justify-content: center;
      align-items: center;
      cursor: pointer;
      list-style-type: none;
      height: 30px;
      line-height: 30px;
      //   box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); /* 添加阴影效果 */
      box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
      transform: translateY(-2px);
      //   padding: 20px;
      background-color: #ffffff;
      margin-bottom: 5px;
      border-radius: 5px;
      transition: transform 0.3s ease-in-out;
      &:hover {
        transform: translateY(-4px);
        box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
        background-color: #ebebeb;
      }
    }
  }
  .graph_container {
    width: 600px;
  }
}
</style>